﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.IO;

namespace Streambolics.PtBurn
{
    /// <summary>
    ///     A PtBurn server located on the network.
    /// </summary>

    public class PtBurnJobServer
    {
        private Dictionary<string, PtBurnJob> _KnownJobs = new Dictionary<string, PtBurnJob> ();
        private DirectoryInfo _RootDir;
        private bool _InUpdate;
        private IniFile _IniFile;
        private List<PtBurnDriveInfo> _Drives;
        private List<PtBurnStackInfo> _Stacks;

        /// <summary>
        ///     Creates a link to a PtBurn server.
        /// </summary>
        /// <param name="aRootDir">
        ///     The name of the network directory where PtBurn
        ///     stores its queue.
        /// </param>

        public PtBurnJobServer (string aRootDir)
        {
            Contract.Requires (!String.IsNullOrEmpty (aRootDir));
            _RootDir = new DirectoryInfo (aRootDir);
        }

        private IniFile StatusFile
        {
            get
            {
                if (_IniFile == null)
                {
                    _IniFile = new IniFile (_RootDir.FullName + "status\\PtStatus.txt");
                }
                return _IniFile;
            }
        }

        internal IniSection SystemSection
        {
            get
            {
                return StatusFile["System"];
            }
        }
        /// <summary>
        ///     The list of jobs we are tracking.
        /// </summary>
        /// <remarks><para>
        ///     It is possible that some jobs in this
        ///     list are unknown to the server, for examples
        ///     jobs that are built locally and not yet 
        ///     submitted.
        /// </para></remarks>

        public IEnumerable<PtBurnJob> Jobs
        {
            get
            {
                UpdateJobs ();
                return _KnownJobs.Values;
            }
        }

        /// <summary>
        ///     Retrieves or creates a job based on a name.
        /// </summary>
        /// <param name="aJobName">
        ///     The name of the job to access.
        /// </param>
        /// <returns>
        ///     A job with the given name.
        /// </returns>

        public PtBurnJob Access (string aJobName)
        {
            UpdateJobs ();
            return InternalAccess (aJobName);
        }

        /// <summary>
        ///     Locate a job by its full job name.
        /// </summary>
        /// <param name="aJobName">
        ///     The name of the job to locate.
        /// </param>
        /// <returns>
        ///     The job, if it is in the list of known jobs,
        ///     null otherwise.
        /// </returns>

        public PtBurnJob Locate (string aJobName)
        {
            UpdateJobs ();
            return InternalLocate (aJobName);
        }

        /// <summary>
        ///     Create a new job for the server.
        /// </summary>
        /// <param name="aJobName">
        ///     The name of the job to create.
        /// </param>
        /// <returns>
        ///     The newly created job.
        /// </returns>
        /// <remarks><para>
        ///     The created job is not yet submitted, so the
        ///     server does not already know about it.
        /// </para><para>
        ///     Call the Submit() method on the job to really
        ///     submit it to the server.
        /// </para></remarks>

        public PtBurnJob Create (string aJobName)
        {
            UpdateJobs ();
            return InternalCreate (aJobName);
        }

        internal void PeriodicUpdate ()
        {
            // UpdateJobs ();
        }

        private void UpdateJobs ()
        {
            if (_InUpdate)
            {
                return;
            }

            _InUpdate = true;
            try
            {
                foreach (PtBurnJob j in _KnownJobs.Values)
                {
                    j.BeginUpdateStatus ();
                }

                foreach (FileInfo f in _RootDir.GetFiles ())
                {
                    PtBurnJobStatus s;
                    string n;
                    if (PtBurnTools.FileNameToJob (f, out n, out s))
                    {
                        PtBurnJob j = InternalAccess (n);
                        j.Status = s;
                    }
                }

                foreach (PtBurnJob j in _KnownJobs.Values)
                {
                    j.EndUpdateStatus ();
                }
            }
            finally
            {
                _InUpdate = false;
            }
        }

        private PtBurnJob InternalLocate (string aJobName)
        {
            Contract.Requires (!String.IsNullOrEmpty (aJobName));

            if (_KnownJobs.ContainsKey (aJobName))
            {
                return _KnownJobs[aJobName];
            }
            else
            {
                return null;
            }
        }

        private PtBurnJob InternalAccess (string aJobName)
        {
            Contract.Requires (!String.IsNullOrEmpty (aJobName));
            Contract.Ensures (_KnownJobs.ContainsKey (aJobName));

            PtBurnJob j = InternalLocate (aJobName);
            if (j == null)
            {
                return InternalCreate (aJobName);
            }
            else
            {
                return j;
            }
        }

        private PtBurnJob InternalCreate (string aJobName)
        {
            Contract.Requires (!String.IsNullOrEmpty (aJobName));
            Contract.Requires (!_KnownJobs.ContainsKey (aJobName));
            Contract.Ensures (_KnownJobs.ContainsKey (aJobName));

            PtBurnJob j = new PtBurnJob (this, aJobName);
            return j;
        }

        internal void Add (PtBurnJob j)
        {
            Contract.Requires (j != null);
            Contract.Requires (!String.IsNullOrEmpty (j.Name));
            _KnownJobs.Add (j.Name, j);
        }

        public DirectoryInfo Directory
        {
            get
            {
                return _RootDir;
            }
        }

        public IList<PtBurnStackInfo> Stacks
        {
            get
            {
                if (_Stacks == null)
                {
                    _Stacks = new List<PtBurnStackInfo> ();
                    _Stacks.Add (new PtBurnStackInfo (this, 0));
                    _Stacks.Add (new PtBurnStackInfo (this, 1));
                }
                return _Stacks;
            }
        }

        public PtBurnStackInfo LeftBin
        {
            get
            {
                return Stacks[0];
            }
        }

        public PtBurnStackInfo RightBin
        {
            get
            {
                return Stacks[1];
            }
        }

        public IList<PtBurnDriveInfo> Drives
        {
            get
            {
                if (_Drives == null)
                {
                    _Drives = new List<PtBurnDriveInfo> ();
                    _Drives.Add (new PtBurnDriveInfo (this, 0));
                    _Drives.Add (new PtBurnDriveInfo (this, 1));
                }
                return _Drives;
            }
        }

        internal IniSection JobSection (string aJobName)
        {
            return StatusFile[aJobName];
        }
    }
}
